package org.rainbowinverted.charging.device.connect.ykc.server.handler;

import org.rainbowinverted.charging.device.api.operation.dto.resp.DeviceChargeFinishReason;
import org.rainbowinverted.charging.device.connect.ykc.server.constants.YkcDeviceChargeStopReasonEnum;
import org.rainbowinverted.charging.device.connect.ykc.server.constants.YkcDeviceGunStatusEnum;
import org.rainbowinverted.charging.device.connect.ykc.server.message.in.YkcDeviceChargeRecordIn;
import org.rainbowinverted.charging.device.connect.ykc.server.message.in.YkcDeviceStatusInfoIn;
import org.rainbowinverted.charging.device.connect.ykc.server.message.in.YkcMessageIn;
import org.rainbowinverted.charging.device.connect.ykc.server.message.out.YkcDeviceChargeRecordOut;
import org.rainbowinverted.charging.device.connect.ykc.server.message.out.YkcDeviceStatusInfoOut;
import org.rainbowinverted.charging.device.connect.ykc.server.util.DelayedExecutorUtil;
import org.rainbowinverted.charging.device.connect.ykc.server.util.SerialNumberGenerator;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.concurrent.CompletableFuture;

/**
 * 设备充电记录处理器
 * @author hehuaiping
 */
@Slf4j
@Component
@AllArgsConstructor
@ChannelHandler.Sharable
public class YkcDeviceChargeRecordHandler extends YkcAbstractHandler<YkcDeviceChargeRecordIn, YkcDeviceChargeRecordOut> {
    private final YkcDeviceGunStatusHandler ykcDeviceGunStatusHandler;
    @Override
    public void read(ChannelHandlerContext channel, YkcMessageIn message) {
        ByteBuf messageBody = message.getMessageBody();
        // 订单编号
        String orderNo = readOrderNo(messageBody);
        // 设备编号
        Long deviceNo = readDeviceNo(messageBody);
        // 枪号
        Integer gunNo = readGunNo(messageBody);
        // 开始时间
        LocalDateTime startTime = readCp56Time2a(messageBody);
        // 结束时间
        LocalDateTime endTime = readCp56Time2a(messageBody);
        // 跳过计费标准和电表计量数据
        messageBody.skipBytes((4 * 16) + (5 * 2));
        // 总电量4位小数
        Integer chargeElectricity = messageBody.readIntLE();
        // 计损电量
        messageBody.skipBytes(4);
        // 充电金额4位小数
        Integer chargeFee = messageBody.readIntLE();
        // 跳过VIN码、交易标识、交易日期
        messageBody.skipBytes(17 + 1 + 7);
        // 停止原因
        YkcDeviceChargeStopReasonEnum stopReasonEnum =  parseStopReason(messageBody.readByte());
        // 物理卡号
        Long cardNo = messageBody.readLongLE();
        YkcDeviceChargeRecordIn chargeRecordIn = YkcDeviceChargeRecordIn.of(orderNo, deviceNo, gunNo, startTime, endTime,
                chargeElectricity, chargeFee, stopReasonEnum, cardNo);
        channel.writeAndFlush(YkcDeviceChargeRecordOut.of(message.getFrameSerialNo(), orderNo, Boolean.TRUE));
        log.info("device charge record info : {}", chargeRecordIn);
        CompletableFuture.runAsync(() -> {
            // 充电结束上报
            chargeFinishedReport(chargeRecordIn);
        }, DelayedExecutorUtil.getExecutor());
    }

    @Override
    public void write(ByteBuf byteBuf, YkcDeviceChargeRecordOut ykcDeviceChargeRecordOut) {
        writeOrderNo(byteBuf, ykcDeviceChargeRecordOut.getOrderNo());
        byteBuf.writeByte(ykcDeviceChargeRecordOut.getAck() ? 0x00 : 0x01);
    }

    private void chargeFinishedReport(YkcDeviceChargeRecordIn chargeRecordIn) {
        YkcDeviceStatusInfoIn ykcDeviceStatusInfoIn = null;
        try {
            // 读取设备实时状态
            ykcDeviceStatusInfoIn = ykcDeviceGunStatusHandler.sendMsg(chargeRecordIn.getDeviceNo(),
                    new YkcDeviceStatusInfoOut(SerialNumberGenerator.nextSerialNumber(),
                            chargeRecordIn.getDeviceNo(), chargeRecordIn.getGunNo()));
        }catch (Exception e) {
            log.error("read device status exception: ", e);
        }
        String gunStatus = YkcDeviceGunStatusEnum.IDLE.getStatus();
        Boolean gunInserted = Boolean.FALSE;
        Integer voltage = 0;
        Integer current = 0;
        if (ykcDeviceStatusInfoIn != null) {
            gunStatus = ykcDeviceStatusInfoIn.getGunStatus();
            gunInserted = ykcDeviceStatusInfoIn.getGunInserted();
            voltage = ykcDeviceStatusInfoIn.getVoltage();
            current = ykcDeviceStatusInfoIn.getCurrent();
        }
        // 充电时长
        int chargeTime = chargeRecordIn.getEndTime().getMinute() - chargeRecordIn.getStartTime().getMinute();
        // 枪状态信息
        ChargeGunInfo gunInfo = new ChargeGunInfo(chargeRecordIn.getGunNo(),
                ykcDeviceGunStatusHandler.parseChargeGunStatus(gunStatus, gunInserted));
        // 枪充电信息
        ChargeGunChargeInfo chargeGunChargeInfo =  ChargeGunChargeInfo.of(chargeRecordIn.getGunNo(),
                chargeRecordIn.getOrderNo(), voltage, current, chargeRecordIn.getChargeElectricity(), (long) chargeTime);
        deviceEventComponent.broadcastDeviceChargeFinishedReport(chargeDetailDTO);
    }

    private DeviceChargeFinishReason parseFinishReason(YkcDeviceChargeStopReasonEnum stopReasonEnum) {
        DeviceChargeFinishReason finishReason = DeviceChargeFinishReason.buildDefault();
        switch (stopReasonEnum) {
            case REMOTE_CLOSE:
                finishReason.setRemoteClose(Boolean.TRUE);
                break;
            case TERMINAL_CLOSE:
                finishReason.setTerminalClose(Boolean.TRUE);
                break;
            case BALANCE_INSUFFICIENT:
                finishReason.setBalanceInsufficient(Boolean.TRUE);
                break;
            case MANUALLY_STOP:
                finishReason.setEmergencyButton(Boolean.TRUE);
                break;
            default:
                log.error("unknown finish reason");
        }
        return finishReason;
    }

    /**
     * 解析停止原因
     * @param stopReasonBytes 停止原因字节
     * @return {@link YkcDeviceChargeStopReasonEnum}
     */
    private YkcDeviceChargeStopReasonEnum parseStopReason(Byte stopReasonBytes) {
        return switch (stopReasonBytes) {
            case 0x40 -> YkcDeviceChargeStopReasonEnum.REMOTE_CLOSE;
            case 0x42 -> YkcDeviceChargeStopReasonEnum.TERMINAL_CLOSE;
            case 0x43 -> YkcDeviceChargeStopReasonEnum.BALANCE_INSUFFICIENT;
            case 0x05 -> YkcDeviceChargeStopReasonEnum.MANUALLY_STOP;
            default -> YkcDeviceChargeStopReasonEnum.OTHER;
        };
    }

}
